﻿jasmine.ConsoleReporter = function(print, doneCallback, showColors) {
    //inspired by mhevery's jasmine-node reporter
    //https://github.com/mhevery/jasmine-node

    doneCallback = doneCallback || function() { };

    var ansi = {
        green: '\033[32m',
        red: '\033[31m',
        yellow: '\033[33m',
        none: '\033[0m'
    },
    language = {
        spec: "spec",
        failure: "failure"
    };

    function coloredStr(color, str) {
        return showColors ? (ansi[color] + str + ansi.none) : str;
    }

    function greenStr(str) {
        return coloredStr("green", str);
    }

    function redStr(str) {
        return coloredStr("red", str);
    }

    function yellowStr(str) {
        return coloredStr("yellow", str);
    }

    function newline() {
        print("\n");
    }

    function started() {
        print("Started");
        newline();
    }

    function greenPass() {
        print(greenStr("PASS"));
    }

    function redFail() {
        print(redStr("FAIL"));
    }

    function yellowStar() {
        print(yellowStr("*"));
    }

    function plural(str, count) {
        return count == 1 ? str : str + "s";
    }

    function repeat(thing, times) {
        var arr = [];
        for (var i = 0; i < times; i++) {
            arr.push(thing);
        }
        return arr;
    }

    function indent(str, spaces) {
        var lines = (str || '').split("\n");
        var newArr = [];
        for (var i = 0; i < lines.length; i++) {
            newArr.push(repeat(" ", spaces).join("") + lines[i]);
        }
        return newArr.join("\n");
    }

    function specFailureDetails(suiteDescription, specDescription, stackTraces) {
        newline();
        print(suiteDescription + " " + specDescription);
        for (var i = 0; i < stackTraces.length; i++) {
            print(indent(stackTraces[i], 2));
        }
    }

    function finished(elapsed) {
        newline();
        print("Finished in " + elapsed / 1000 + " seconds");
    }

    function summary(colorF, specs, failed) {
        newline();
        print(colorF(specs + " " + plural(language.spec, specs) + ", " +
      failed + " " + plural(language.failure, failed)));
    }

    function greenSummary(specs, failed) {
        summary(greenStr, specs, failed);
    }

    function redSummary(specs, failed) {
        summary(redStr, specs, failed);
    }

    function fullSuiteDescription(suite) {
        var fullDescription = suite.description;
        if (suite.parentSuite) fullDescription = fullSuiteDescription(suite.parentSuite) + " " + fullDescription;
        return fullDescription;
    }

    this.now = function() {
        return new Date().getTime();
    };

    this.reportRunnerStarting = function() {
        this.runnerStartTime = this.now();
        started();
    };

    this.reportSpecStarting = function() { /* do nothing */
    };

    this.reportSpecResults = function(spec) {
        var results = spec.results();
        if (results.skipped) {
            yellowStar();
        } else {
            if (results.passed()) {
                print('#' + spec.id + ' ' + spec.suite.description + ': ' + spec.description);
                greenPass();
            } else {
                print(redStr('#' + spec.id + ' ' + spec.suite.description + ': ' + spec.description));
                redFail();
            }
        }
    };

    this.suiteResults = [];

    this.reportSuiteResults = function(suite) {
        var suiteResult = {
            description: fullSuiteDescription(suite),
            failedSpecResults: []
        };

        suite.results().items_.forEach(function(spec) {
            if (spec.failedCount > 0 && spec.description) suiteResult.failedSpecResults.push(spec);
        });

        this.suiteResults.push(suiteResult);
    };

    function eachSpecFailure(suiteResults, callback) {
        for (var i = 0; i < suiteResults.length; i++) {
            var suiteResult = suiteResults[i];
            for (var j = 0; j < suiteResult.failedSpecResults.length; j++) {
                var failedSpecResult = suiteResult.failedSpecResults[j];
                var stackTraces = [];
                for (var k = 0; k < failedSpecResult.items_.length; k++) stackTraces.push(failedSpecResult.items_[k].trace.stack);
                callback(suiteResult.description, failedSpecResult.description, stackTraces);
            }
        }
    }

    this.reportRunnerResults = function(runner) {
        eachSpecFailure(this.suiteResults, function(suiteDescription, specDescription, stackTraces) {
            specFailureDetails(suiteDescription, specDescription, stackTraces);
        });

        finished(this.now() - this.runnerStartTime);

        var results = runner.results();
        var summaryFunction = results.failedCount === 0 ? greenSummary : redSummary;
        summaryFunction(runner.specs().length, results.failedCount);
        doneCallback(runner);
    };
};